package com.sojson.config.mybatis.typehandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import org.springframework.stereotype.Component;

import com.sojson.enums.EDbType;
import com.sojson.project.addr.entity.bo.Point;
import com.sojson.util.ExpUtil;
import com.sojson.util.StringUtil;
import com.sojson.util.db.DbUtil;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKBReader;

import cn.hutool.core.util.ArrayUtil;
import oracle.sql.STRUCT;

/**
 * 空间坐标转换器
 * 
 * @author liu
 * @date 2022-01-20
 */
@Component
@MappedTypes(Point.class)
@MappedJdbcTypes(JdbcType.STRUCT)
public class PointTypeHandler implements TypeHandler<Point> {

    /**
     * 设置参数
     */
    @Override
    public void setParameter(PreparedStatement ps, int i, Point parameter, JdbcType jdbcType) throws SQLException {
        Double lon = parameter.getLon();
        Double lat = parameter.getLat();
        ps.setString(i, lon + " " + lat);
    }

    /**
     * 设置返回值
     */
    @Override
    public Point getResult(ResultSet rs, String columnName) throws SQLException {
        String dbType = DbUtil.getDBType();

        if (dbType == EDbType.MYSQL.getName()) {
            return mysqlConvers(rs.getObject(columnName));
        } else if (dbType == EDbType.ORACLE.getName()) {
            return oracleConvers(rs.getObject(columnName));
        } else if (dbType == EDbType.DB2.getName()) {
            return db2Convers(rs.getString(columnName));
        }

        return mysqlConvers(rs.getObject(columnName));
    }

    /**
     * 设置返回值
     */
    @Override
    public Point getResult(ResultSet rs, int columnIndex) throws SQLException {
        String dbType = DbUtil.getDBType();

        if (dbType == EDbType.MYSQL.getName()) {
            return mysqlConvers(rs.getObject(columnIndex));
        } else if (dbType == EDbType.ORACLE.getName()) {
            return oracleConvers(rs.getObject(columnIndex));
        } else if (dbType == EDbType.DB2.getName()) {
            return db2Convers(rs.getString(columnIndex));
        }

        return mysqlConvers(rs.getObject(columnIndex));
    }

    /**
     * 设置返回值
     */
    @Override
    public Point getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String dbType = DbUtil.getDBType();

        if (dbType == EDbType.MYSQL.getName()) {
            return mysqlConvers(cs.getObject(columnIndex));
        } else if (dbType == EDbType.ORACLE.getName()) {
            return oracleConvers(cs.getObject(columnIndex));
        } else if (dbType == EDbType.DB2.getName()) {
            return db2Convers(cs.getString(columnIndex));
        }

        return mysqlConvers(cs.getObject(columnIndex));
    }

    /**
     * Mysql转换
     * 
     * @param obj
     * @return
     */
    private Point mysqlConvers(Object obj) {
        byte[] bs = obj instanceof byte[] ? ((byte[])obj) : null;

        if (StringUtil.isBlankObject(bs)) {
            return null;
        }
        int len = bs.length;
        if (len < 5) {
            return null;
        }

        byte[] bytes = new byte[len - 4];
        System.arraycopy(bs, 4, bytes, 0, len - 4);
        WKBReader reader = new WKBReader();
        Geometry read = null;
        try {
            read = reader.read(bytes);
        } catch (ParseException e) {
            ExpUtil.throwEx(e);
        }
        com.vividsolutions.jts.geom.Point point =
            read instanceof com.vividsolutions.jts.geom.Point ? ((com.vividsolutions.jts.geom.Point)read) : null;
        return conversPoint(point.toString());
    }

    /**
     * Oracle转换
     * 
     * @param obj
     * @return
     */
    private Point oracleConvers(Object obj) {
        STRUCT struct = obj instanceof STRUCT ? ((STRUCT)obj) : null;

        if (StringUtil.isBlankObject(struct)) {
            return null;
        }
        Object[] attributes = null;
        try {
            attributes = struct.getAttributes();

            if (StringUtil.isBlankObject(attributes) || attributes.length < 3) {
                return null;
            }

            STRUCT s = (STRUCT)attributes[2];
            Object[] a = s.getAttributes();

            return new Point(Double.parseDouble(a[0].toString()), Double.parseDouble(a[1].toString()));
        } catch (SQLException e) {
            ExpUtil.throwEx(e);
            return null;
        }
    }

    /**
     * DB2转换
     * 
     * @param point
     * @return
     */
    private Point db2Convers(String point) {
        return conversPoint(point);
    }

    /**
     * 转换成对象
     * 
     * @param point
     * @return
     */
    private Point conversPoint(String point) {
        if (StringUtil.isBlankString(point)) {
            return null;
        }

        String pointStr = point.replaceFirst("POINT", "").replaceFirst("\\(", "").replaceFirst("\\)", "");
        String[] split = pointStr.trim().split(" ");
        if (ArrayUtil.isEmpty(split) || split.length != 2) {
            return null;
        }

        return new Point(Double.parseDouble(split[0]), Double.parseDouble(split[1]));
    }

}